//Copyright 2011 - Jonathan Allen

using System;
using Granite.Modeling.Internals;
using System.Collections.Generic;
using System.ComponentModel;
using Granite.ComponentModel;
using System.Diagnostics.Contracts;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;

namespace Granite.Modeling
{
	/// <summary>
	/// This ModelBase tracks which fields have changed since the last time AcceptChanges or RejectChanges was called. The purpose of this ModelBase is to easy to determine which objects have unsaved changes.
	/// </summary>
	public class ChangeTrackingModelBase : ModelBase<ChangeTrackingPropertyBag>, IRevertibleChangeTracking, IChangeTrackingGraph
	{
		bool m_IsChanged;

		/// <summary>
		/// This ModelBase tracks which fields have changed since the last time Checkpoint or Revert was called.
		/// </summary>
		public ChangeTrackingModelBase()
		{
			Properties.PropertyChanged += (s, e) => IsChanged = true;
		}

		/// <summary>
		/// Returns True if any fields were modified since the last call to AcceptChanges. This does not walk the object graph.
		/// </summary>
		public bool IsChanged
		{
			get { return m_IsChanged; }
			protected set
			{
				if (m_IsChanged == value)
					return;
				m_IsChanged = value;
				InvokePropertyChanged(CommonProperties.IsChangedProperty);
			}
		}

		/// <summary>
		/// Walk the object graph, looking for changed items.
		/// </summary>
		/// <returns></returns>
		[Pure]
		public bool IsChangedGraph()
		{
			if (IsChanged)
				return true;
			return Properties.IsChangedGraph();
		}

		/// <summary>
		/// Marks all fields as unchanged and clears the IsChanged flag.
		/// </summary>
		/// <remarks>This will call AcceptChanges on properties that implement IChangeTracking</remarks>
		public void AcceptChanges()
		{
			Properties.AcceptChanges(true);
			IsChanged = false;
		}

		/// <summary>
		/// Discards all pending changes and reverts to the values used the last time AcceptChanges was called.
		/// </summary>
		/// <remarks>This will call RejectChanges on properties that implement IRevertibleChangeTracking</remarks>
		public void RejectChanges()
		{
			Properties.RejectChanges(true);
			IsChanged = false;
		}

  //      /// <summary>
  //      /// Updates the model using the dictionary and accepts the changes. 
  //      /// </summary>
  //      /// <param name="fields"></param>
		///// <param name="updateMode"></param>
  //      /// <remarks>This is usually called as a result of a stored procedure call. Said stored procedure only needs to return altered fields.</remarks>
  //      public void Update(IDictionary<string, object> fields, UpdateMode updateMode)
  //      {
  //          if (fields == null)
  //              throw new ArgumentNullException("fields", "fields is null.");

  //          Load(fields);

  //          if (updateMode == UpdateMode.AcceptChanges)
  //              AcceptChanges();
  //          else if (updateMode == UpdateMode.AcceptLocalChanges)
  //              Properties.AcceptChanges(false);
  //      }

		[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode"), SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
		[ContractInvariantMethod]
		void ObjectInvariant()
		{

		}

		[OnDeserialized]
		void OnDeserialized(StreamingContext context)
		{
			AcceptChanges();
		}

	}

}
